/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mopso;

import util.Diversity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import mopso.DominanceOperators.Dominance;
import mopso.ParticleData.Sorting;

/**
 *
 * @author robson
 */
public class Repository {

    private DominanceOperators dominanceOperator;
    private Diversity diversityOperator;

    public Repository() {
        dominanceOperator = new DominanceOperators();
        diversityOperator = new Diversity();
    }

    public Particle updatePbest(Particle particle, Problem problem) {

        Particle auxParticle1 = (Particle) particle.clone();
        Particle auxParticleCurrentPbest = new Particle(problem);
        Particle auxParticle2 = new Particle(problem);

        ArrayList<ParticleData> pbestClone;
        Dominance dominance;

        for (int j = 0; j < particle.getPbests().size(); j++) {

            auxParticleCurrentPbest.setParticleData(particle.getPbests().get(j));

            dominance = dominanceOperator.verifyDominace(auxParticle1, auxParticleCurrentPbest, problem);

            if (dominance == Dominance.DOMINATED) {
                return particle;
            }
        }

        pbestClone = new ArrayList<ParticleData>();

        for (int i = 0; i < particle.getPbests().size(); i++) {
            pbestClone.add((ParticleData) particle.getPbests().get(i).clone());
        }

        particle.getPbests().clear();

        for (int i = 0; i < pbestClone.size(); i++) {

            auxParticle2.setParticleData(pbestClone.get(i));

            dominance = dominanceOperator.verifyDominace(auxParticle2, auxParticle1, problem);

            if (dominance == Dominance.DOMINATED) {
                pbestClone.get(i).setDominatedPbest(true);

            }

        }

        for (int i = 0; i < pbestClone.size(); i++) {
            if (!pbestClone.get(i).isDominatedPbest()) {
                particle.getPbests().add((ParticleData) pbestClone.get(i).clone());
            }
        }

        particle.getPbests().add((ParticleData) particle.getParticleData().clone());

        pbestClone.clear();

        return particle;
    }

    public ArrayList<Particle> initializeExternalArchive(ArrayList<Particle> externalArchive, ArrayList<Particle> swarm, Problem problem) {

        ArrayList<Particle> swarmClone;
        Dominance dominance;

        swarmClone = new ArrayList<Particle>();

        for (int i = 0; i < swarm.size(); i++) {
            swarmClone.add((Particle) ((Particle) swarm.get(i)).clone());
        }

        for (int i = 0; i < swarmClone.size(); i++) {

            if (!swarmClone.get(i).isDominated()) {

                for (int j = 0; j < swarmClone.size(); j++) {

                    if ((swarmClone.get(i) != swarmClone.get(j)) &&
                            !swarmClone.get(j).isDominated()) {
                        dominance = dominanceOperator.verifyDominace(swarmClone.get(i), swarmClone.get(j), problem);

                        if (dominance == Dominance.DOMINATES) {
                            swarmClone.get(j).setDominated(true);
                        } else if (dominance == Dominance.DOMINATED) {
                            swarmClone.get(i).setDominated(true);
                            j = swarmClone.size();
                        }
                    }
                }
            }
        }

        for (int i = 0; i < swarmClone.size(); i++) {
            if (!swarmClone.get(i).isDominated()) {
                externalArchive.add((Particle) ((Particle) swarmClone.get(i)).clone());
            }
        }

        swarmClone.clear();

        return externalArchive;

    }

    public ArrayList<Particle_CSS> initializeExternalArchive_CSS(ArrayList<Particle_CSS> externalArchive, ArrayList<Particle_CSS> swarm, Problem problem) {

        ArrayList<Particle_CSS> swarmClone;
        Dominance dominance;

        swarmClone = new ArrayList<Particle_CSS>();

        for (int i = 0; i < swarm.size(); i++) {
            swarmClone.add((Particle_CSS) ((Particle_CSS) swarm.get(i)).clone());
        }

        for (int i = 0; i < swarmClone.size(); i++) {

            if (!swarmClone.get(i).isDominated()) {
                for (int j = 0; j < swarmClone.size(); j++) {

                    if ((swarmClone.get(i) != swarmClone.get(j)) &&
                            !swarmClone.get(j).isDominated()) {
                        dominance = dominanceOperator.verifyDominace(swarmClone.get(i), swarmClone.get(j), problem);

                        if (dominance == Dominance.DOMINATES) {
                            swarmClone.get(j).setDominated(true);
                        } else if (dominance == Dominance.DOMINATED) {
                            swarmClone.get(i).setDominated(true);
                            j = swarmClone.size();
                        }
                    }
                }
            }
        }

        for (int i = 0; i < swarmClone.size(); i++) {
            if (!swarmClone.get(i).isDominated()) {
                externalArchive.add((Particle_CSS) ((Particle_CSS) swarmClone.get(i)).clone());
            }
        }

        swarmClone.clear();

        return externalArchive;
    }

    public ArrayList<Particle> qualityExternalArchive(ArrayList<Particle> externalArchive, Problem problem) {
        externalArchive = diversityOperator.calculateCrowdingDistance(externalArchive, problem);
        return externalArchive;
    }

    public ArrayList<Particle_CSS> qualityExternalArchive_CSS(ArrayList<Particle_CSS> externalArchive, Problem problem) {
        externalArchive = diversityOperator.calculateCrowdingDistance_CSS(externalArchive, problem);
        return externalArchive;
    }

    public ArrayList<Particle> updateExternalArchive(ArrayList<Particle> externalArchive, ArrayList<Particle> swarm, Problem problem) {

//        if ((currentIteration % 100) == 99) {
//            this.plotExternalArchive(currentIteration + 1);
//            this.plotSwarm(currentIteration + 1);
//            System.out.println("debug");
//        }

        ArrayList<Particle> auxParticleList;
        ArrayList<Particle> newLeaders;
        Dominance dominance;

        newLeaders = new ArrayList<Particle>();
        auxParticleList = new ArrayList<Particle>();

        for (int i = 0; i < swarm.size(); i++) {
            auxParticleList.add((Particle) ((Particle) swarm.get(i)).clone());
        }

        for (int i = 0; i < auxParticleList.size(); i++) {

            if (!auxParticleList.get(i).isDominated()) {

                for (int j = 0; j < auxParticleList.size(); j++) {

                    if ((auxParticleList.get(i) != auxParticleList.get(j)) &&
                            !auxParticleList.get(j).isDominated()) {
                        dominance = dominanceOperator.verifyDominace(auxParticleList.get(i), auxParticleList.get(j), problem);

                        if (dominance == Dominance.DOMINATES) {
                            auxParticleList.get(j).setDominated(true);
                        } else if (dominance == Dominance.DOMINATED) {
                            auxParticleList.get(i).setDominated(true);
                            j = auxParticleList.size();
                        }
                    }
                }
            }
        }

        for (int i = 0; i < auxParticleList.size(); i++) {
            if (!auxParticleList.get(i).isDominated()) {
                newLeaders.add((Particle) ((Particle) auxParticleList.get(i)).clone());
            //externalArchive.add((Particle) swarmClone.get(i).clone());
            } else {
                auxParticleList.get(i).setDominated(false);
            }
        }

        auxParticleList.clear();

        for (int i = 0; i < externalArchive.size(); i++) {
            auxParticleList.add((Particle) ((Particle) externalArchive.get(i)).clone());
        }

        externalArchive.clear();

        for (int i = 0; i < auxParticleList.size(); i++) {

            if (!auxParticleList.get(i).isDominated()) {

                for (int j = 0; j < newLeaders.size(); j++) {

                    if ( /* (externalArchiveClone.get(i) != newLeaders.get(j)) && */!newLeaders.get(j).isDominated()) {
                        dominance = dominanceOperator.verifyDominace(auxParticleList.get(i), newLeaders.get(j), problem);

                        if (dominance == Dominance.DOMINATES) {
                            newLeaders.get(j).setDominated(true);
                        } else if (dominance == Dominance.DOMINATED) {
                            auxParticleList.get(i).setDominated(true);
                            j = newLeaders.size();
                        }
                    }
                }
            }
        }

        for (int i = 0; i < auxParticleList.size(); i++) {
            if (!auxParticleList.get(i).isDominated()) {
                externalArchive.add((Particle) ((Particle) auxParticleList.get(i)).clone());
            }
        }

        for (int i = 0; i < newLeaders.size(); i++) {
            if (!newLeaders.get(i).isDominated()) {
                externalArchive.add((Particle) ((Particle) newLeaders.get(i)).clone());
            }
        }

        auxParticleList.clear();
        newLeaders.clear();

//        if (externalArchive.size() == 1) {
//            System.out.println("Arquivo externo com apenas um elemento");
//            System.out.println("Problema: " + problem.getName());
//            System.out.println("Iteração: " + currentIteration);
//            System.out.println("Enxame...");
//            this.printParticles(swarm);
//            System.out.println("Arquivo Externo...");
//            this.printParticles(externalArchive);
//            BufferedInputStream reader = new BufferedInputStream(System.in);
//
//            try {
//                reader.read();
//            } catch (IOException ex) {
//                Logger.getLogger(MOPSO.class.getName()).log(Level.SEVERE, null, ex);
//            }
//        }

        return externalArchive;
    }

    public ArrayList<Particle_CSS> updateExternalArchive_CSS(ArrayList<Particle_CSS> externalArchive, ArrayList<Particle_CSS> swarm, Problem problem) {

        ArrayList<Particle_CSS> auxParticleList;
        ArrayList<Particle_CSS> newLeaders;
        Dominance dominance;

        newLeaders = new ArrayList<Particle_CSS>();
        auxParticleList = new ArrayList<Particle_CSS>();

        for (int i = 0; i < swarm.size(); i++) {
            auxParticleList.add((Particle_CSS) ((Particle_CSS) swarm.get(i)).clone());
        }

        for (int i = 0; i < auxParticleList.size(); i++) {

            if (!auxParticleList.get(i).isDominated()) {

                for (int j = 0; j < auxParticleList.size(); j++) {

                    if ((auxParticleList.get(i) != auxParticleList.get(j)) &&
                            !auxParticleList.get(j).isDominated()) {
                        dominance = dominanceOperator.verifyDominace(auxParticleList.get(i), auxParticleList.get(j), problem);

                        if (dominance == Dominance.DOMINATES) {
                            auxParticleList.get(j).setDominated(true);
                        } else if (dominance == Dominance.DOMINATED) {
                            auxParticleList.get(i).setDominated(true);
                            j = auxParticleList.size();
                        }
                    }
                }
            }
        }

        for (int i = 0; i < auxParticleList.size(); i++) {
            if (!auxParticleList.get(i).isDominated()) {
                newLeaders.add((Particle_CSS) ((Particle_CSS) auxParticleList.get(i)).clone());
            } else {
                auxParticleList.get(i).setDominated(false);
            }
        }

        auxParticleList.clear();

        for (int i = 0; i < externalArchive.size(); i++) {
            auxParticleList.add((Particle_CSS) ((Particle_CSS) externalArchive.get(i)).clone());
        }

        externalArchive.clear();

        for (int i = 0; i < auxParticleList.size(); i++) {

            if (!auxParticleList.get(i).isDominated()) {

                for (int j = 0; j < newLeaders.size(); j++) {

                    if ( /* (externalArchiveClone.get(i) != newLeaders.get(j)) && */!newLeaders.get(j).isDominated()) {
                        dominance = dominanceOperator.verifyDominace(auxParticleList.get(i), newLeaders.get(j), problem);

                        if (dominance == Dominance.DOMINATES) {
                            newLeaders.get(j).setDominated(true);
                        } else if (dominance == Dominance.DOMINATED) {
                            auxParticleList.get(i).setDominated(true);
                            j = newLeaders.size();
                        }
                    }
                }
            }
        }

        for (int i = 0; i < auxParticleList.size(); i++) {
            if (!auxParticleList.get(i).isDominated()) {
                externalArchive.add((Particle_CSS) ((Particle_CSS) auxParticleList.get(i)).clone());
            }
        }

        for (int i = 0; i < newLeaders.size(); i++) {
            if (!newLeaders.get(i).isDominated()) {
                externalArchive.add((Particle_CSS) ((Particle_CSS) newLeaders.get(i)).clone());
            }
        }

        auxParticleList.clear();
        newLeaders.clear();

        return externalArchive;
    }

    public ArrayList<Particle> removeDominatedParticlesExternalArchive(ArrayList<Particle> externalArchive, Problem problem) {

        ArrayList<Particle> externalArchiveClone;
        Dominance dominance;

        externalArchiveClone = new ArrayList<Particle>();

        for (int i = 0; i < externalArchive.size(); i++) {
            externalArchiveClone.add((Particle) ((Particle) externalArchive.get(i)).clone());
        }

        for (int i = 0; i < externalArchiveClone.size(); i++) {

            if (!externalArchiveClone.get(i).isDominated()) {

                for (int j = 0; j < externalArchiveClone.size(); j++) {

                    if ((externalArchiveClone.get(i) != externalArchiveClone.get(j)) &&
                            !externalArchiveClone.get(j).isDominated()) {
                        dominance = dominanceOperator.verifyDominace(externalArchiveClone.get(i), externalArchiveClone.get(j), problem);

                        if (dominance == Dominance.DOMINATES) {
                            externalArchiveClone.get(j).setDominated(true);
                        } else if (dominance == Dominance.DOMINATED) {
                            externalArchiveClone.get(i).setDominated(true);
                            j = externalArchiveClone.size();
                        }
                    }
                }
            }
        }

        externalArchive.clear();

        for (int i = 0; i < externalArchiveClone.size(); i++) {
            if (!externalArchiveClone.get(i).isDominated()) {
                externalArchive.add((Particle) ((Particle) externalArchiveClone.get(i)).clone());
            }
        }

        externalArchiveClone.clear();

        return externalArchive;
    }

    public ArrayList<Particle> trunkExternalArchive(ArrayList<Particle> externalArchive, int nMaxParticlesExternalArchive) {

        // se o tamanho do arquivo externo for maior que o máximo, remove o excesso
        if (externalArchive.size() > nMaxParticlesExternalArchive) {

//            System.out.println("Tamanho do arquivo externo: " + externalArchive.size());

            for (int i = externalArchive.size() - 1; i >= nMaxParticlesExternalArchive; i--) {
                externalArchive.remove(i);
            }
//            System.out.println("Tamanho do arquivo externo: " + externalArchive.size());
        }

        return externalArchive;
    }
    
    public ArrayList<Particle_CSS> trunkExternalArchive_CSS(ArrayList<Particle_CSS> externalArchive, int nMaxParticlesExternalArchive) {

        // se o tamanho do arquivo externo for maior que o máximo, remove o excesso
        if (externalArchive.size() > nMaxParticlesExternalArchive) {

//            System.out.println("Tamanho do arquivo externo: " + externalArchive.size());

            for (int i = externalArchive.size() - 1; i >= nMaxParticlesExternalArchive; i--) {
                externalArchive.remove(i);
            }
//            System.out.println("Tamanho do arquivo externo: " + externalArchive.size());
        }

        return externalArchive;
    }

    public ArrayList<Particle> trunkExternalArchive_MOPSO(AdaptiveGrid adaptiveGrid, ArrayList<Particle> externalArchive, Problem problem, int nMaxParticlesExternalArchive) {

        // se o tamanho do arquivo externo for maior que o máximo, remove o excesso
        if (externalArchive.size() > nMaxParticlesExternalArchive) {

//            System.out.println("Tamanho do arquivo externo: " + externalArchive.size());

            for (int i = externalArchive.size() - 1; i >= nMaxParticlesExternalArchive; i--) {

                ArrayList mappedCellswithParticles = new ArrayList();
                ArrayList<Particle> particlesInMappedCellSelected = new ArrayList<Particle>();
                MappedCells mappedCellSelected = new MappedCells();
                Random r = new Random();
                Particle particleToRemove = new Particle(problem);

                double sum;
                double rouletteValue;
                double lowerPositionedValue, higherPositionedValue;
                int k;

                for (int j = 0; j < adaptiveGrid.getMappedCells().length; j++) {
                    if (adaptiveGrid.getMappedCells()[j].getNSolutionsInCell() > 0) {
                        mappedCellswithParticles.add(adaptiveGrid.getMappedCells()[j]);
                    }
                }

                sum = 0;
                for (k = 0; k < mappedCellswithParticles.size(); k++) {
                    sum += ((MappedCells) mappedCellswithParticles.get(k)).getNSolutionsInCell();
                }

                rouletteValue = Math.random() * sum;
                lowerPositionedValue = 0.0;
                higherPositionedValue = 0.0;

                for (k = 0; k < mappedCellswithParticles.size(); k++) {
                    higherPositionedValue += ((MappedCells) mappedCellswithParticles.get(k)).getNSolutionsInCell();

                    if ((rouletteValue >= lowerPositionedValue) && (rouletteValue < higherPositionedValue)) {

                        mappedCellSelected = ((MappedCells) mappedCellswithParticles.get(k));

                        k = mappedCellswithParticles.size();
                    }
                    lowerPositionedValue = higherPositionedValue;
                }

                for (int l = 0; l < externalArchive.size(); l++) {
                    boolean isEqual = true;

                    for (int m = 0; m < problem.getNObjectives(); m++) {
//                int num = externalArchive.get(l).getParticleData().getAdaptiveGridIndexes()[m];
//                num = mappedCellSelected.getLineGridMapped()[m];
                        if (externalArchive.get(l).getParticleData().getAdaptiveGridIndexes()[m] != mappedCellSelected.getLineGridMapped()[m]) {
                            isEqual = false;
                            break;
                        }
                    }

                    if (isEqual) {
                        particlesInMappedCellSelected.add(externalArchive.get(l));
                    }
                }

                particleToRemove = particlesInMappedCellSelected.get(r.nextInt(particlesInMappedCellSelected.size()));

                for (int l = 0; l < externalArchive.size(); l++) {
                    if (this.equalParticles(particleToRemove, externalArchive.get(l))) {
                        externalArchive.remove(l);

                        for (int m = 0; m < adaptiveGrid.getMappedCells().length; m++) {
                            if (this.equalGrids(adaptiveGrid.getMappedCells()[m], mappedCellSelected)) {
                                adaptiveGrid.getMappedCells()[m].setNSolutionsInCell(adaptiveGrid.getMappedCells()[m].getNSolutionsInCell() - 1);
                                break;
                            }
                        }
                        break;
                    }
                }
            }
        }

        return externalArchive;
    }

    public Particle selectLeader_DNPSO(Particle particle, ArrayList<Particle> externalArchive, int F1, int F2, int MNEARESTNEIGHBORS) {

        ArrayList<Particle> neighbors;

//        double sum;
//        double rouletteValue;
//
//        double lowerPositionedValue, higherPositionedValue;
//        int k;

//        System.out.println( "externalArchive.size(): " + externalArchive.size() );

        neighbors = new ArrayList<Particle>();


        for (int i = 0; i < externalArchive.size(); i++) {
            if (!this.equalParticles(particle, externalArchive.get(i))) {
                neighbors.add(externalArchive.get(i));
            }
        }

        if (neighbors.size() > 0) {

            for (int j = 0; j < neighbors.size(); j++) {
                neighbors.get(j).getParticleData().setDistanceFromNeighbor(this.calculateDistanceNeighbor(particle, neighbors.get(j), F1));
                neighbors.get(j).getParticleData().setSortType(ParticleData.Sorting.SORTBYNEIGHBORDISTANCE);
            }

            Collections.sort(neighbors);

//            System.out.println("Neighbors before trunk: " + neighbors.size());
            neighbors = this.trunkNeighbors(neighbors, MNEARESTNEIGHBORS);
//            System.out.println("Neighbors after trunk: " + neighbors.size());

            // escolha do líder pela partícula com menor fitness em relação à F2 a partir dos m vizinhos        
            for (int j = 0; j < neighbors.size(); j++) {
                neighbors.get(j).getParticleData().setSortType(ParticleData.Sorting.SORTBYFITNESS);
                neighbors.get(j).getParticleData().setComparableDimension(F2);
            }

            Collections.sort(neighbors);

            particle.setLeader((Particle) ((Particle) neighbors.get(0)).clone());


//            // escolha do líder atraves de roleta baseado no fitness da F2 a partir dos m vizinhos        
//            sum = 0;
//            for (k = 0; k < neighbors.size(); k++) {
//                sum += neighbors.get(k).getParticleData().getFitness()[F2];
//            }
//
//            if (sum != 0) {
//                rouletteValue = Math.random() * sum;
//                lowerPositionedValue = 0.0;
//                higherPositionedValue = 0.0;
//
//                for (k = 0; k < neighbors.size(); k++) {
//                    higherPositionedValue += neighbors.get(k).getParticleData().getFitness()[F2];
//
//                    if ((rouletteValue >= lowerPositionedValue) && (rouletteValue < higherPositionedValue)) {
//                        particle.setLeader((Particle) ((Particle) neighbors.get(k)).clone());
//
//                        k = neighbors.size();
//                    }
//                    lowerPositionedValue = higherPositionedValue;
//                }
//
//            } else {
//                k = (int) (Math.random() * neighbors.size());
//                particle.setLeader((Particle) ((Particle) neighbors.get(k)).clone());
//            }
        }
        
        else {
            particle.setLeader((Particle) particle.clone());
        }

        return particle;
    }

    public Particle selectLeader(Particle particle, ArrayList<Particle> externalArchive) {

        double sum;
        double rouletteValue;




        double lowerPositionedValue, higherPositionedValue;
        int k;

        sum = 0;
        for (k = 0; k < externalArchive.size(); k++) {
            sum += externalArchive.get(k).getParticleData().getCrowdingDistance();
        }

        if (sum != 0) {
            rouletteValue = Math.random() * sum;
            lowerPositionedValue = 0.0;
            higherPositionedValue = 0.0;

            for (k = 0; k < externalArchive.size(); k++) {
                higherPositionedValue += externalArchive.get(k).getParticleData().getCrowdingDistance();

                if ((rouletteValue >= lowerPositionedValue) && (rouletteValue < higherPositionedValue)) {
                    particle.setLeader(externalArchive.get(k));

                    k = externalArchive.size();
                }
                lowerPositionedValue = higherPositionedValue;
            }

        } else {
            k = (int) (Math.random() * externalArchive.size());
            particle.setLeader(externalArchive.get(k));
        }
        return particle;
    }

    public Particle selectLeader_MOPSO(Particle particle, AdaptiveGrid adaptiveGrid, ArrayList<Particle> externalArchive, Problem problem) {
        ArrayList mappedCellswithParticles = new ArrayList();
        ArrayList<Particle> particlesInMappedCellSelected = new ArrayList<Particle>();
        MappedCells mappedCellSelected = new MappedCells();
        Random r = new Random();

        double sum;
        double rouletteValue;
        double lowerPositionedValue, higherPositionedValue;
        int k;

        for (int i = 0; i < adaptiveGrid.getMappedCells().length; i++) {
            if (adaptiveGrid.getMappedCells()[i].getFitnessDiversity() != 0.0) {
                mappedCellswithParticles.add(adaptiveGrid.getMappedCells()[i]);
            }
        }

        sum = 0;
        for (k = 0; k < mappedCellswithParticles.size(); k++) {
            sum += ((MappedCells) mappedCellswithParticles.get(k)).getFitnessDiversity();
        }

        rouletteValue = Math.random() * sum;
        lowerPositionedValue = 0.0;
        higherPositionedValue = 0.0;

        for (k = 0; k < mappedCellswithParticles.size(); k++) {
            higherPositionedValue += ((MappedCells) mappedCellswithParticles.get(k)).getFitnessDiversity();

            if ((rouletteValue >= lowerPositionedValue) && (rouletteValue < higherPositionedValue)) {

                mappedCellSelected = ((MappedCells) mappedCellswithParticles.get(k));

                k = mappedCellswithParticles.size();
            }
            lowerPositionedValue = higherPositionedValue;
        }

        for (int l = 0; l < externalArchive.size(); l++) {
            boolean isEqual = true;

            for (int m = 0; m < problem.getNObjectives(); m++) {
//                int num = externalArchive.get(l).getParticleData().getAdaptiveGridIndexes()[m];
//                num = mappedCellSelected.getLineGridMapped()[m];
                if (externalArchive.get(l).getParticleData().getAdaptiveGridIndexes()[m] != mappedCellSelected.getLineGridMapped()[m]) {
                    isEqual = false;
                    break;
                }
            }

            if (isEqual) {
                particlesInMappedCellSelected.add(externalArchive.get(l));
            }
        }

        particle.setLeader(particlesInMappedCellSelected.get(r.nextInt(particlesInMappedCellSelected.size())));

        return particle;
    }

    public Particle selectLeaderPbest(Particle particle) {

        if (particle.getPbests().size() == 1) {
            particle.setLeaderPbest(particle.getPbests().get(0));
        } else if (particle.getPbests().size() == 0) {
            particle.getPbests().add((ParticleData) ((ParticleData) particle.getParticleData()).clone());
            particle.setLeaderPbest(particle.getPbests().get(0));
        } else {
            double sum;
            double rouletteValue;

            double lowerPositionedValue, higherPositionedValue;
            int k;

            sum = 0;

            for (k = 0; k < particle.getPbests().size(); k++) {
                sum += particle.getPbests().get(k).getCrowdingDistance();
            }

//            sum = particle.getPbests().size();

            if (sum != 0) {
                rouletteValue = Math.random() * sum;
                lowerPositionedValue = 0.0;
                higherPositionedValue = 0.0;

                for (k = 0; k < particle.getPbests().size(); k++) {
                    higherPositionedValue += particle.getPbests().get(k).getCrowdingDistance();
//                higherPositionedValue = k + 1;

                    if ((rouletteValue >= lowerPositionedValue) && (rouletteValue < higherPositionedValue)) {
                        particle.setLeaderPbest(particle.getPbests().get(k));

                        k = particle.getPbests().size();
                    }
                    lowerPositionedValue = higherPositionedValue;
                }
            } else {
                k = (int) (Math.random() * particle.getPbests().size());
                particle.setLeaderPbest(particle.getPbests().get(k));
            }
        }
        return particle;
    }

    public Particle qualityPbest(Particle particle, Problem problem) {
        particle.setPbests(diversityOperator.calculateCrowdingDistanceParticleData(particle.getPbests(), problem));
        return particle;
    }

    public Particle trunkPbest(Particle particle, int nMaxPbests) {
        // se o tamanho do arquivo externo for maior que o máximo, remove o excesso
        if (particle.getPbests().size() > nMaxPbests) {

            for (int i = particle.getPbests().size() - 1; i >= nMaxPbests; i--) {
                particle.getPbests().remove(i);
            }
        }
        return particle;
    }

    public Particle selectLeaderCD(Particle particle, ArrayList<Particle> externalArchive, Problem problem) {
        ArrayList<Particle> tenPercentlessCrowdedExternalArchive;
        int tenPercentExternalArchiveSize, index, toSort;
        Dominance dominance;
        Random r;

        tenPercentlessCrowdedExternalArchive = new ArrayList<Particle>();
        tenPercentExternalArchiveSize = (int) (externalArchive.size() * 0.1);
        toSort = tenPercentExternalArchiveSize;
        index = 0;
        r = new Random();

        if (tenPercentExternalArchiveSize > 0) {
            while ((toSort > 0) && (index < externalArchive.size())) {

                dominance = dominanceOperator.verifyDominace(particle, externalArchive.get(index), problem);

                if (dominance == Dominance.DOMINATED) {
                    tenPercentlessCrowdedExternalArchive.add((Particle) (((Particle) externalArchive.get(index)).clone()));
                    toSort--;
                }
                index++;
            }

            if (toSort == 0) {
                particle.setLeader(tenPercentlessCrowdedExternalArchive.get(r.nextInt(tenPercentExternalArchiveSize)));
            } else {
                particle.setLeader(externalArchive.get(r.nextInt(externalArchive.size())));
            }
        } else {
            particle.setLeader(externalArchive.get(r.nextInt(externalArchive.size())));
        }

        return particle;
    }

    public Particle updatePbest_CDLS(Particle particle, Problem problem) {

        Particle auxParticle1 = (Particle) particle.clone();
        Particle auxParticleCurrentPbest = new Particle(problem);

        Dominance dominance;

        auxParticleCurrentPbest.setParticleData(particle.getPbests().get(0));

        dominance = dominanceOperator.verifyDominace(auxParticle1, auxParticleCurrentPbest, problem);

        if (dominance == Dominance.DOMINATES) {
            particle.getPbests().clear();
            particle.getPbests().add(0, (ParticleData) ((ParticleData) particle.getParticleData()).clone());
        } else if (dominance == Dominance.INCOMPARABLE) {
            if (Math.random() > 0.5) {
                particle.getPbests().clear();
                particle.getPbests().add(0, (ParticleData) ((ParticleData) particle.getParticleData()).clone());
            }
        }

        return particle;
    }

    public Particle updatePbest_DNPSO(Particle particle, Problem problem) {

        Particle auxParticle1 = (Particle) particle.clone();
        Particle auxParticleCurrentPbest = new Particle(problem);

        Dominance dominance;

        auxParticleCurrentPbest.setParticleData(particle.getPbests().get(0));

        dominance = dominanceOperator.verifyDominace(auxParticle1, auxParticleCurrentPbest, problem);

        if (dominance == Dominance.DOMINATES) {
            particle.getPbests().clear();
            particle.getPbests().add(0, (ParticleData) ((ParticleData) particle.getParticleData()).clone());
        }

        return particle;
    }

    public Particle updatePbest_Test(Particle particle, Problem problem) {

        Particle auxParticle1 = (Particle) particle.clone();
        Particle auxParticleCurrentPbest = new Particle(problem);

        Dominance dominance;

        auxParticleCurrentPbest.setParticleData(particle.getPbests().get(0));

        dominance = dominanceOperator.verifyDominace(auxParticle1, auxParticleCurrentPbest, problem);

        if (dominance == Dominance.DOMINATES) {
            particle.getPbests().clear();
            particle.getPbests().add(0, (ParticleData) ((ParticleData) particle.getParticleData()).clone());
        } else if (dominance == Dominance.INCOMPARABLE) {
//            if (Math.random() > 0.5) {
//                particle.getPbests().clear();
//                particle.getPbests().add(0, (ParticleData) ((ParticleData) particle.getParticleData()).clone());
//            }
        }

        return particle;
    }

    public Particle updatePbestByNearCD(Particle particle, ArrayList<Particle> externalArchive, Problem problem) {
        Particle auxParticle1 = (Particle) particle.clone();
        Particle auxParticleCurrentPbest = new Particle(problem);
        double crowdingDistanceNearCurrentPbest;
        double crowdingDistanceNearCandidatePbest;

        Dominance dominance;

        auxParticleCurrentPbest.setParticleData(particle.getPbests().get(0));

        dominance = dominanceOperator.verifyDominace(auxParticle1, auxParticleCurrentPbest, problem);

        if (dominance == Dominance.DOMINATES) {
            particle.getPbests().clear();
            particle.getPbests().add(0, (ParticleData) ((ParticleData) particle.getParticleData()).clone());
        } else if (dominance == Dominance.INCOMPARABLE) {
            crowdingDistanceNearCurrentPbest = this.calculateCrowndingDistanceByNearExternalArchive(particle.getPbests().get(0), externalArchive, problem);
            crowdingDistanceNearCandidatePbest = this.calculateCrowndingDistanceByNearExternalArchive(particle.getParticleData(), externalArchive, problem);
            
            if( crowdingDistanceNearCandidatePbest > crowdingDistanceNearCurrentPbest ) {
                particle.getPbests().clear();
                particle.getPbests().add(0, (ParticleData) ((ParticleData) particle.getParticleData()).clone());                
            }


//            if (Math.random() > 0.5) {
//                particle.getPbests().clear();
//                particle.getPbests().add(0, (ParticleData) ((ParticleData) particle.getParticleData()).clone());
//            }
        }

        return particle;
    }

    private double calculateCrowndingDistanceByNearExternalArchive(ParticleData particleData, ArrayList<Particle> externalArchive, Problem problem) {
        double crowndingDistanceByNear;
        double sum;
        double euclidianDistance;
        double minDistance = Double.MAX_VALUE;
        double[] normalizedPositionParticle = new double[problem.getNObjectives()];
        double[] normalizedPositionExternalArchiveParticle = new double[problem.getNObjectives()];
        int indexMinDistance;
        double[] valueMax = new double[problem.getNObjectives()];
        double[] valueMin = new double[problem.getNObjectives()];
        
        indexMinDistance = 0;

        for (int i = 0; i < problem.getNObjectives(); i++) {
            valueMax[i] = diversityOperator.getDimensionMaxValue(externalArchive, i);
            valueMin[i] = diversityOperator.getDimensionMinValue(externalArchive, i);
            
            if( valueMax[i] < particleData.getFitness()[i] ) {
                valueMax[i] = particleData.getFitness()[i];
            }
            
            else if( valueMin[i] > particleData.getFitness()[i] ) {
                valueMin[i] = particleData.getFitness()[i];
            }
            
            normalizedPositionParticle[i] = ( particleData.getFitness()[i] - valueMin[i] ) / ( valueMax[i] - valueMin[i] );
        }

        for (int j = 0; j < externalArchive.size(); j++) {
            if (!this.equalParticleData(particleData, externalArchive.get(j).getParticleData())) {
                sum = 0.0;

                for (int i = 0; i < problem.getNObjectives(); i++) {
                    normalizedPositionExternalArchiveParticle[i] = ( externalArchive.get(j).getParticleData().getFitness()[i] - valueMin[i] ) / ( valueMax[i] - valueMin[i] );
                    
                    sum += Math.pow( normalizedPositionParticle[i] - normalizedPositionExternalArchiveParticle[i], 2);
                }

                euclidianDistance = Math.sqrt(sum);

                if ( euclidianDistance < minDistance ) {
                    minDistance = euclidianDistance;
                    indexMinDistance = j;
                }
            }
        }

        crowndingDistanceByNear = externalArchive.get(indexMinDistance).getParticleData().getCrowdingDistance();

        return crowndingDistanceByNear;
    }

    public double[] calculateDatumPoint(ArrayList<Particle_CSS> externalArchive, Problem problem) {
        double[] datumPoint = new double[problem.getNObjectives()];

        for (int i = 0; i < problem.getNObjectives(); i++) {
            datumPoint[i] = diversityOperator.getDimensionMaxValue_CSS(externalArchive, i);
        }

        return datumPoint;
    }

    public Particle_CSS selectGBest1(Particle_CSS particle_CSS, ArrayList<Particle_CSS> externalArchive, double[] datumPoint, Problem problem) {
        int indexMaxExternalArchiveCosAngle = 0;
        double angleValue;
        double maxCosAngle = -Double.MAX_VALUE;

        for (int i = 0; i < externalArchive.size(); i++) {
            angleValue = this.getCosAngleValue(particle_CSS, externalArchive.get(i), datumPoint, problem);

            if (angleValue > maxCosAngle) {
                maxCosAngle = angleValue;
                indexMaxExternalArchiveCosAngle = i;
            }
        }

        particle_CSS.setGBest1(externalArchive.get(indexMaxExternalArchiveCosAngle));

        return particle_CSS;
    }

    public ArrayList<Particle_CSS> selectGBest2(ArrayList<Particle_CSS> swarm, ArrayList<Particle_CSS> externalArchive, Problem problem) {
        Random r = new Random();
        int selecetedObjective = r.nextInt(problem.getNObjectives());

        for (int j = 0; j < swarm.size(); j++) {
            swarm.get(j).getParticleData().setComparableDimension(selecetedObjective);
            swarm.get(j).getParticleData().setSortType(Sorting.SORTBYFITNESS);
        }

        Collections.sort(swarm);

        for (int i = 0; i < externalArchive.size(); i++) {
            externalArchive.get(i).getParticleData().setComparableDimension(selecetedObjective);
            externalArchive.get(i).getParticleData().setSortType(Sorting.SORTBYFITNESS);
        }

        Collections.sort(externalArchive, Collections.reverseOrder());

        for (int j = 0; j < swarm.size(); j = j + 2) {

            boolean leaderFinded = false;

            for (int k = 0; k < externalArchive.size(); k++) {

                if (swarm.get(j).getParticleData().getFitness()[selecetedObjective] >= externalArchive.get(k).getParticleData().getFitness()[selecetedObjective]) {
                    swarm.get(j).setGBest2(externalArchive.get(k));
                    leaderFinded = true;
                    k = externalArchive.size();
                }
            }

            if (!leaderFinded) {
                swarm.get(j).setGBest2(externalArchive.get(externalArchive.size() - 1));
            }
        }

        Collections.sort(externalArchive);

        for (int j = 1; j < swarm.size(); j = j + 2) {

            boolean leaderFinded = false;

            for (int k = 0; k < externalArchive.size(); k++) {

                if (swarm.get(j).getParticleData().getFitness()[selecetedObjective] <= externalArchive.get(k).getParticleData().getFitness()[selecetedObjective]) {
                    swarm.get(j).setGBest2(externalArchive.get(k));
                    leaderFinded = true;
                    k = externalArchive.size();
                }
            }

            if (!leaderFinded) {
                swarm.get(j).setGBest2(externalArchive.get(externalArchive.size() - 1));
            }
        }

        return swarm;
    }

    public ArrayList<Particle_CSS> executeClusterMethod(ArrayList<Particle_CSS> externalArchive, Problem problem) {
        double radiusCluster;
        ArrayList<Particle_CSS> externalArchiveClone = new ArrayList<Particle_CSS>();
        Particle_CSS selectedParticle;
        Particle_CSS current;
        double distance;
        
        Random r = new Random();

        radiusCluster = calculateRadiusCluster(externalArchive, problem);

        for (int i = 0; i < externalArchive.size(); i++) {
            externalArchiveClone.add((Particle_CSS) ((Particle_CSS) externalArchive.get(i)).clone());
        }

        while (externalArchiveClone.size() > 0) {
            int index = r.nextInt(externalArchiveClone.size());
            selectedParticle = externalArchiveClone.get(index);
            externalArchiveClone.remove(selectedParticle);

            for (int i = 0; i < externalArchiveClone.size(); i++) {
//                if (i != index) {
                    current = externalArchiveClone.get(i);
                    distance = this.calculateEuclidianDistance( selectedParticle.getParticleData().getFitness(), current.getParticleData().getFitness() );
                    if (distance < radiusCluster) {
                        externalArchiveClone.remove(i);
                        externalArchive.remove(current);
                    }
//                }
            }
        }

        return externalArchive;
    }

    private double calculateEuclidianDistance(double[] d1, double[] d2) {
        double sum = 0.0;
        
        for ( int i = 0; i < d1.length; i++ ) {
            sum += Math.pow( d1[i] - d2[i], 2);
        }
        
        return Math.sqrt(sum);
    }

    private double calculateNorm(double[] vector) {
        double innerProduct = 0;

        for (int i = 0; i < vector.length; i++) {
            innerProduct += (vector[i] + vector[i]);
        }

        return Math.sqrt(innerProduct);
    }

    private double calculateRadiusCluster(ArrayList<Particle_CSS> externalArchive, Problem problem) {
        double[] minValues = new double[problem.getNObjectives()];
        double[] maxValues = new double[problem.getNObjectives()];
        double[] d1 = new double[2];
        double[] d2 = new double[2];

        double sumDistances = 0.0;

        for (int i = 0; i < problem.getNObjectives(); i++) {
            minValues[i] = diversityOperator.getDimensionMinValue_CSS(externalArchive, i);
            maxValues[i] = diversityOperator.getDimensionMaxValue_CSS(externalArchive, i);
        }

        for (int j = 0; j < problem.getNObjectives() - 1; j++) {
            d1[0] = minValues[j];
            d1[1] = maxValues[j + 1];

            d2[0] = maxValues[j];
            d2[1] = minValues[j + 1];

            sumDistances += this.calculateEuclidianDistance(d1, d2);
        }

        return sumDistances / (2 * externalArchive.size());
    }
    
    private double calculateDistanceNeighbor(Particle particle, Particle neighbor, int F1) {
        double distance;

        distance = particle.getParticleData().getFitness()[F1] - neighbor.getParticleData().getFitness()[F1];

        return Math.abs(distance);
    }

    private boolean equalGrids(MappedCells mappedCell1, MappedCells mappedCell2) {
        boolean isEquals = true;

        for (int i = 0; i < mappedCell1.getLineGridMapped().length; i++) {
            if (mappedCell1.getLineGridMapped()[i] != mappedCell2.getLineGridMapped()[i]) {
                isEquals = false;
                i = mappedCell1.getLineGridMapped().length;
            }
        }

        return isEquals;
    }

    public boolean equalParticles(Particle particle1, Particle particle2) {
        boolean isEquals = true;

        for (int i = 0; i < particle1.getParticleData().getPositions().length; i++) {
            if (particle1.getParticleData().getPositions()[i] != particle2.getParticleData().getPositions()[i]) {
                isEquals = false;
                i = particle1.getParticleData().getPositions().length;
            }
        }

        return isEquals;
    }
    
    public boolean equalParticles(Particle_CSS particle1, Particle_CSS particle2) {
        boolean isEquals = true;

        for (int i = 0; i < particle1.getParticleData().getPositions().length; i++) {
            if (particle1.getParticleData().getPositions()[i] != particle2.getParticleData().getPositions()[i]) {
                isEquals = false;
                i = particle1.getParticleData().getPositions().length;
            }
        }

        return isEquals;
    }

    public boolean equalParticleData(ParticleData particleData1, ParticleData particleData2) {
        boolean isEquals = true;

        for (int i = 0; i < particleData1.getPositions().length; i++) {
            if (particleData1.getPositions()[i] != particleData2.getPositions()[i]) {
                isEquals = false;
                i = particleData1.getPositions().length;
            }
        }
        return isEquals;
    }
    
    private double getCosAngleValue(Particle_CSS particle_CSS, Particle_CSS aExternalArchiveParticle, double[] datumPoint, Problem problem) {
        double[] datumExternalArchiveLine = new double[problem.getNObjectives()];
        double[] particleExternalArchiveLine = new double[problem.getNObjectives()];
        double cosAngleValue;

        for (int i = 0; i < problem.getNObjectives(); i++) {
            datumExternalArchiveLine[i] = datumPoint[i] - aExternalArchiveParticle.getParticleData().getFitness()[i];
            particleExternalArchiveLine[i] = Math.abs(particle_CSS.getParticleData().getFitness()[i] - aExternalArchiveParticle.getParticleData().getFitness()[i]);
        }

        if (!this.equalParticles(particle_CSS, aExternalArchiveParticle)) {
            cosAngleValue = this.getCosAngleValue(datumExternalArchiveLine, particleExternalArchiveLine);
        } else {
            cosAngleValue = 1.0;
        }

        return cosAngleValue;
    }

    private double getCosAngleValue(double[] datumExternalArchiveLine, double[] particleExternalArchiveLine) {
        double innerProduct = 0.0;
        double datumExternalArchiveLineNorm;
        double particleExternalArchiveLineNorm;

        for (int i = 0; i < datumExternalArchiveLine.length; i++) {
            innerProduct += (datumExternalArchiveLine[i] * particleExternalArchiveLine[i]);
        }

        datumExternalArchiveLineNorm = this.calculateNorm(datumExternalArchiveLine);
        particleExternalArchiveLineNorm = this.calculateNorm(particleExternalArchiveLine);

        return (innerProduct / (datumExternalArchiveLineNorm * particleExternalArchiveLineNorm));
    }

    private ArrayList<Particle> trunkNeighbors(ArrayList<Particle> neighbors, int MNEARESTNEIGHBORS) {

        // se o tamanho do arquivo externo for maior que o máximo, remove o excesso
        if (neighbors.size() > MNEARESTNEIGHBORS) {

            for (int i = neighbors.size() - 1; i >= MNEARESTNEIGHBORS; i--) {
                neighbors.remove(i);
            }
        }
        return neighbors;
    }
}

